package com.badshun.ojs;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class OJSVar extends OJSObject {
    public enum VAR_TYPE {
        Int,
        Float,
        String,
        Bool,
        MathExpression,
        BoolException,
        Unclear
    }

    public static final String[] matchRegExeption = {
            "^\\d+$", // Ints
            "^\\d*\\.\\d*$", // Float 1.2 or 1. or .2 or .
            "^\"((((?!\").)*)?)\"$", // String
            "true|false", // Bool
    };

    public static final char[] mathExpressionToken = {
            // lower priority
            '+',
            '-',
            // higher priority
            '*',
            '/'
    };

    public static final String[] boolExpressionToken = {
            // low priority
            ">",
            "<",
            "!",
            ">=",
            "<=",
            "==",
            "!=",
            // higher priority
            "&&",
            "||",
    };

    public TYPE type = TYPE.VAR;
    public VAR_TYPE varType = VAR_TYPE.Unclear;

    private OJSRuntime runtime;

    public OJSVar(String name, String value, OJSRuntime runtime) {
        super(runtime);

        this.runtime = runtime;

        if (!checkName(name)) {
            // name starts with digital
            // /throws exception
            System.out.println("name can not starts with digital");
        }

        this.name = name;
        this.content = value;

        matchValue(content);
    }

    private VAR_TYPE matchValue(String content) {
        boolean isMatched = false;
        VAR_TYPE var_type = VAR_TYPE.Unclear;

        for (int i = 0; i < matchRegExeption.length; i++) {
            Matcher matcher = getMatcher(content, matchRegExeption[i]);

            if (matcher.find()) {
                isMatched = true;

                if (i == 0) {
                    varType = VAR_TYPE.Int;
                } else if (i == 1) {
                    varType = VAR_TYPE.Float;
                } else if (i == 2) {
                    varType = VAR_TYPE.String;

                    content = matcher.group(1);
                } else if (i == 3) {
                    varType = VAR_TYPE.Bool;
                }

                var_type = varType;
                System.out.println("content:" + content + ", type:" + varType);
            }
        }

        if (!isMatched) {
            boolean isMathExpression = false;
            boolean isBoolExpression = false;

            for (int i = 0; i < mathExpressionToken.length; i++) {
                if (OJSUtils.hasChar(content, mathExpressionToken[i])) {
                    isMathExpression = true;
                    var_type = VAR_TYPE.MathExpression;
                }
            }

            if (!isMathExpression) {
                for (int i = 0; i < boolExpressionToken.length; i++) {
                    if (OJSUtils.has(content, boolExpressionToken[i])) {
                        isBoolExpression = true;
                        var_type = VAR_TYPE.BoolException;
                    }
                }
            }

            if (isMathExpression) {
                System.out.println("is math expression");

                String result = calcMultipleMathExpression(content);
                content = result;
                System.out.println("result is " + result);

            } else if (isBoolExpression) {

            }
        }

        return var_type;
    }

    private boolean checkName(String name) {
        if (name.charAt(0) >= '0' && name.charAt(0) <= '9') {
            return false;
        } else {
            return true;
        }
    }

    private Matcher getMatcher(String content, String regexRule) {
        Pattern pattern = Pattern.compile(regexRule);
        Matcher matcher = pattern.matcher(content);

        return matcher;
    }

    private String getValue(String simpleMathExpression) {
        String regexRule = "\\w+";
        Pattern pattern = Pattern.compile(regexRule);
        Matcher matcher = pattern.matcher(simpleMathExpression);

        while (matcher.find()) {
            String token = matcher.group(0);

            if (!OJSUtils.isDigital(token)) {
                if (token.equals("true") || token.equals("false")) {
                    //
                } else {
                    String value = runtime.getInMemory(type, token);
                    simpleMathExpression = simpleMathExpression.replace(token, value);
                }
            }
        }

        return simpleMathExpression;
    }

    private String calcMultipleMathExpression(String multipleMathExpression) {
        String content = "";
        boolean hasBracket = false;

        if (OJSUtils.hasChar(multipleMathExpression, '(')) {
            content = OJSUtils.getBracketContent(multipleMathExpression, "()");
            hasBracket = true;
        } else {
            content = multipleMathExpression;
        }

        String result = calcSimpleMathExpression(content);

        if (hasBracket) {
            multipleMathExpression = multipleMathExpression.replace("(" + content + ")", result);

            return calcMultipleMathExpression(multipleMathExpression);
        } else {
            multipleMathExpression = result;
        }

        return multipleMathExpression;
    }

    private String calcSimpleMathExpression(String simpleMathExpression) {
        simpleMathExpression = simpleMathExpression.replaceAll(" ", "");
        simpleMathExpression = getValue(simpleMathExpression);
        boolean hasHighPriority = OJSUtils.hasChar(simpleMathExpression, '*')
                || OJSUtils.hasChar(simpleMathExpression, '/');
        String tokenRegister = "";
        int registerOperatorCount = 0;
        char opeartor = '+';
        String leftNumber = "";

        for (int i = 0; i < simpleMathExpression.length(); i++) {
            char token = simpleMathExpression.charAt(i);
            boolean isMathOperator = OJSUtils.isMathOperator(token);
            boolean isDigital = OJSUtils.isDigital(token + "");
            boolean isDot = token == '.';

            if (!isMathOperator && !isDigital && !isDot) {
                // throws exception
                System.out.println("unknown token [" + token + "]");
                return null;
            }

            if (tokenRegister.length() == 0) {
                tokenRegister += token;
            } else {
                if ((OJSUtils.isDigital(token + "") || token == '.')
                        && (OJSUtils.isDigital(tokenRegister) || tokenRegister.length() == 1)) {
                    tokenRegister += token;
                }

                if (OJSUtils.isMathOperator(token)) {
                    if (hasHighPriority && (token == '+' || token == '-') && registerOperatorCount == 0) {
                        tokenRegister = "";

                        continue;
                    }

                    if (registerOperatorCount == 0) {
                        leftNumber = tokenRegister;
                        tokenRegister = "";
                        opeartor = token;
                        registerOperatorCount = 1;
                    } else if (registerOperatorCount == 1) {
                        String rightNumber = tokenRegister;
                        String result = OJSUtils.calc(leftNumber + "", rightNumber + "", opeartor);
                        result = simpleMathExpression.replace(leftNumber + opeartor + rightNumber, result);

                        if (OJSUtils.isDigital(result)) {
                            return result;
                        } else {
                            return calcSimpleMathExpression(result);
                        }
                    }
                }
            }

            if (i == simpleMathExpression.length() - 1) {
                if (registerOperatorCount == 1) {
                    String rightNumber = tokenRegister;
                    String result = OJSUtils.calc(leftNumber + "", rightNumber + "", opeartor);
                    result = simpleMathExpression.replace(leftNumber + opeartor + rightNumber, result);

                    if (OJSUtils.isDigital(result)) {
                        return result;
                    } else {
                        return calcSimpleMathExpression(result);
                    }

                } else {
                    if (OJSUtils.isDigital(tokenRegister)) {
                        return tokenRegister;
                    }
                }
            }
        }
        return null;
    }

    private String calcSimpleBoolExpression(String simpleBoolExpression) {
        simpleBoolExpression = simpleBoolExpression.replaceAll(" ", "");
        simpleBoolExpression = getValue(simpleBoolExpression);
        String[] members = simpleBoolExpression.split(">=|<=|>|<|!=|!|==|&&|\\|\\|");

        for (int i = 0; i < members.length; i++) {
            VAR_TYPE var_type = matchValue(members[i]);

            if (var_type == VAR_TYPE.MathExpression) {
                String mathExpressionResult = calcMultipleMathExpression(members[i]);
                return calcMultipleMathExpression(simpleBoolExpression.replace(members[i], mathExpressionResult));
            }
        }

        String leftTokens = "";
        String tokenRegister = "";
        String operator = "";
        char[] operatorTokens = { '>', '<', '!', '=', '&', '|' };
        boolean hasHighPriority = OJSUtils.hasChar(simpleBoolExpression, '&')
                || OJSUtils.hasChar(simpleBoolExpression, '|');

        for (int i = 0; i < simpleBoolExpression.length(); i++) {
            char token = simpleBoolExpression.charAt(i);

            for (int j = 0; j < operatorTokens.length; j++) {
                if (token == operatorTokens[j]) {
                    if (operator.length() == 0) {
                        if (leftTokens.length() == 0) {
                            leftTokens = tokenRegister;
                            tokenRegister = "";
                        } else {
                            String rightTokens = tokenRegister;
                            tokenRegister = "";

                            if (hasHighPriority) {
                                if (operator.equals("&&") || operator.equals("||")) {
                                    // start calcing
                                } else {
                                    leftTokens = rightTokens;
                                    operator = "";
                                    rightTokens = "";
                                }
                            } else {
                                // start calcing
                            }
                        }
                    }

                    operator += token;
                } else {
                    tokenRegister += token;
                }
            }
        }

        return null;
    }

    @Override
    public TYPE getType() {
        return type;
    }
}
